package com.canaan.business.controller.pc.inOutStock;

import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.util.StrUtil;
import com.canaan.business.common.enums.agv.AgvEnum;
import com.canaan.business.common.enums.inOutStock.ErpBillTypeEnum;
import com.canaan.business.common.enums.inOutStock.InOrderStatusEnum;
import com.canaan.business.common.enums.inOutStock.OutOrderStatusEnum;
import com.canaan.business.common.enums.storehouse.StorehouseStockStatusEnum;
import com.canaan.business.common.helper.EmptyStockHelper;
import com.canaan.business.domain.basic.WmsStorehouseLocation;
import com.canaan.business.domain.bill.*;
import com.canaan.business.domain.inOutStock.*;
import com.canaan.business.domain.storehouse.WmsStorehouseStock;
import com.canaan.business.domain.vo.NewSplitBillTrayVo;
import com.canaan.business.domain.vo.SplitBillTrayVo;
import com.canaan.business.domain.vo.TraySplitBillDetailVo;
import com.canaan.business.service.agv.IAgvTaskDispatchService;
import com.canaan.business.service.basic.IWmsStorehouseLocationService;
import com.canaan.business.service.bill.*;
import com.canaan.business.service.inOutStock.*;
import com.canaan.business.service.storehouse.IWmsStorehouseStockService;
import com.canaan.common.core.redis.RedisCache;
import com.canaan.common.utils.StringUtils;
import com.canaan.common.utils.TableNoUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.canaan.common.annotation.Log;
import com.canaan.common.core.controller.BaseController;
import com.canaan.common.core.domain.AjaxResult;
import com.canaan.common.enums.BusinessType;

import javax.annotation.Resource;

import com.canaan.common.utils.poi.ExcelUtil;
import com.canaan.common.core.page.TableDataInfo;

/**
 * 分捡单Controller
 *
 * @author canaan
 * @date 2025-03-29
 */
@Api(tags = "分捡单")
@RestController
@RequestMapping("/inOutStock/splitBill")
public class SplitBillController extends BaseController {
    @Resource
    private ISplitBillService splitBillService;
    @Resource
    private ISplitBillDetailService splitBillDetailService;
    @Resource
    private ITrayService trayService;
    @Resource
    private ITrayDetailService trayDetailService;
    @Resource
    private IBindRecordService bindRecordService;
    @Resource
    private IWmsStorehouseLocationService locationService;
    @Resource
    private IWmsStorehouseStockService stockService;

    @Resource
    private IReturnStockBillService returnStockBillService;
    @Resource
    private IReturnStockBillDetailService returnStockBillDetailService;
    @Resource
    private IOutStockBillService outStockBillService;
    @Resource
    private IOutStockBillDetailService outStockBillDetailService;
    @Resource
    private IPurchaseReturnBillService purchaseReturnBillService;
    @Resource
    private IAdjustOutBillService adjustOutBillService;
    @Resource
    private IAgvTaskDispatchService agvTaskDispatchService;
    @Resource
    private IModulationOutBillService modulationOutBillService;
    @Resource
    private EmptyStockHelper emptyStockHelper;

    @Resource
    private RedisCache redisCache;

    /**
     * 查询分捡单列表
     */
    @ApiOperation(value = "查询分捡单列表")
    @PreAuthorize("@ss.hasPermi('inOutStock:splitBill:list')")
    @GetMapping("/list")
    public TableDataInfo list(SplitBill splitBill) {
        startPage();
        List<SplitBill> list = splitBillService.selectSplitBillList(splitBill);
        return getDataTable(list);
    }

    /**
     * 导出分捡单列表
     */
    @ApiOperation(value = "导出分捡单列表")
    @PreAuthorize("@ss.hasPermi('inOutStock:splitBill:export')")
    @Log(title = "分捡单", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SplitBill splitBill) {
        List<SplitBill> list = splitBillService.selectSplitBillList(splitBill);
        ExcelUtil<SplitBill> util = new ExcelUtil<SplitBill>(SplitBill.class);
        util.exportExcel(response, list, "分捡单数据");
    }

    /**
     * 获取分捡单详细信息
     */
    @ApiOperation(value = "获取分捡单详细信息")
    @PreAuthorize("@ss.hasPermi('inOutStock:splitBill:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(splitBillService.selectSplitBillById(id));
    }

    /**
     * 新增分捡单
     */
    @ApiOperation(value = "新增分捡单")
    @PreAuthorize("@ss.hasPermi('inOutStock:splitBill:add')")
    @Log(title = "分捡单", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody SplitBill splitBill) {
        return toAjax(splitBillService.insertSplitBill(splitBill));
    }

    /**
     * 修改分捡单
     */
    @ApiOperation(value = "修改分捡单")
    @PreAuthorize("@ss.hasPermi('inOutStock:splitBill:edit')")
    @Log(title = "分捡单", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SplitBill splitBill) {
        return toAjax(splitBillService.updateSplitBill(splitBill));
    }

    /**
     * 删除分捡单
     */
    @ApiOperation(value = "删除分捡单")
    @PreAuthorize("@ss.hasPermi('inOutStock:splitBill:remove')")
    @Log(title = "分捡单", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        return toAjax(splitBillService.deleteSplitBillByIds(ids));
    }

    @ApiOperation(value = "执行分拣")
    @Log(title = "执行分拣", businessType = BusinessType.UPDATE)
    @PostMapping("/split")
    @Transactional
    public AjaxResult split(@RequestBody SplitBillTrayVo splitBillTrayVo) {
        logger.info("开始分拣：{}", splitBillTrayVo.toString());

        String key = "IN_LOCK";
        //TODO 临时方案锁，后期处理
        String lock = redisCache.getCacheObject(key);
        if (StringUtils.equals(lock, "true")) {
            throw new RuntimeException("有任务正在进行中，请稍后再试");
        }
        redisCache.setCacheObject(key, "true");
        try {
            return splitLock(splitBillTrayVo);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
            //TODO 临时方案
            redisCache.setCacheObject(key, "false");
        }
    }

    private AjaxResult splitLock(SplitBillTrayVo splitBillTrayVo) {
        Tray tray = trayService.selectTrayByTrayNo(splitBillTrayVo.getTrayNo());
        if (tray == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "不存在");
        }
        WmsStorehouseStock stock = stockService.selectWmsStorehouseStockByTrayNo(splitBillTrayVo.getNewTrayNo());
        if (stock != null) {
            return error("托盘号" + splitBillTrayVo.getNewTrayNo() + "在仓库已经存在");
        }
        //TODO 判断新托盘是否存在数据,如果存在数据,则判断是否是此订单数据
        Tray newTray = trayService.selectTrayByTrayNo(splitBillTrayVo.getNewTrayNo());
        if (newTray != null && !StrUtil.equalsIgnoreCase(newTray.getErpOutBillNo(), tray.getErpOutBillNo())) {
            return error("托盘号" + splitBillTrayVo.getNewTrayNo() + "已经绑定,订单号:" + newTray.getErpOutBillNo() + "的出库单,请更换新托盘");
        }
        String erpOutBillNo = tray.getErpOutBillNo();
        if (ErpBillTypeEnum.PURCHASE_RETURN.getCode().equals(splitBillTrayVo.getBillType())) {
            PurchaseReturnBill purchaseReturnBill = purchaseReturnBillService.selectPurchaseReturnBillByNo(erpOutBillNo);
            if (purchaseReturnBill == null) {
                return error("采购退货单" + erpOutBillNo + "不存在");
            }
        } else if (ErpBillTypeEnum.ADJUST_OUT.getCode().equals(splitBillTrayVo.getBillType())) {
            AdjustOutBill adjustOutBill = adjustOutBillService.selectAdjustOutBillByNo(erpOutBillNo);
            if (adjustOutBill == null) {
                return error("调拨出库单" + erpOutBillNo + "不存在");

            }
        } else if (ErpBillTypeEnum.MODULATION_OUT.getCode().equals(splitBillTrayVo.getBillType())) {
            ModulationOutBill modulationOutBill = modulationOutBillService.selectModulationOutBillByNo(erpOutBillNo);
            if (modulationOutBill == null) {
                return error("出库调整单" + erpOutBillNo + "不存在");

            }
        }
        OutStockBill outStockBillQuery = new OutStockBill();
        outStockBillQuery.setTrayNo(splitBillTrayVo.getTrayNo());
        outStockBillQuery.setDelFlag("0");
        List<OutStockBill> oldStockBills = outStockBillService.selectOutStockBillList(outStockBillQuery);
        if (oldStockBills == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "出库单不存在");
        }
        OutStockBill oldOutStockBill = oldStockBills.get(0);
        if (!OutOrderStatusEnum.ACCOMPLISH.getCode().equals(oldOutStockBill.getOrderStatus())) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "出库单还未完成");
        }
        WmsStorehouseStock sourceStock = stockService.selectWmsStorehouseStockByTrayNo(splitBillTrayVo.getTrayNo());
        if (sourceStock == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "库位不存在");
        }
        WmsStorehouseLocation sourceLocation = locationService.selectWmsStorehouseLocationByNo(sourceStock.getStorehouseLocationNo());
        if (sourceLocation == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "库位" + sourceStock.getStorehouseLocationNo() + "不存在");
        }
        String productNo = "";
        TrayDetail query = new TrayDetail();
        query.setTrayNo(splitBillTrayVo.getTrayNo());
        query.setDelFlag("0");
        Map<String, TrayDetail> trayDetailMap = new HashMap();
        //托盘明细
        List<TrayDetail> oldTrayDetails = trayDetailService.selectTrayDetailList(query);
        SplitBillDetail detailQuery = new SplitBillDetail();
        detailQuery.setOrderNo(tray.getSplitBillNo());
        detailQuery.setDelFlag("0");
        for (TrayDetail item : oldTrayDetails) {
            trayDetailMap.put(item.getProductNo() + "_" + item.getBatchNo(), item);
            productNo = item.getProductNo();
        }
        //分拣单
        SplitBill splitBill = splitBillService.selectSplitBillByNo(tray.getSplitBillNo());
        if (splitBill == null) {
            return error("托盘号" + tray.getTrayNo() + "不存在分拣单数据");
        }
        List<SplitBillDetail> splitBillDetails = splitBillDetailService.selectSplitBillDetailList(detailQuery);
        if (splitBillDetails == null || splitBillDetails.size() == 0) {
            return error("托盘号" + tray.getTrayNo() + "不存在分拣单明细数据");
        }
        //分拣单状态更新为已完成
        splitBill.setOrderStatus(OutOrderStatusEnum.ACCOMPLISH.getCode());
        splitBillService.updateSplitBill(splitBill);
        //将旧托盘的出库单据数据清空
        tray.setSplitBillNo("");
        tray.setErpOutBillNo("");
        tray.setOutBillNo("");
        trayService.updateTray(tray);
        //生成退库单
        ReturnStockBill returnStockBill = new ReturnStockBill();
        returnStockBill.setNo("BACK"+TableNoUtil.getNo(ReturnStockBill.class));
        WmsStorehouseLocation destLocation = emptyStockHelper.analyseInStock(returnStockBill.getNo(), productNo, true, false);
        returnStockBill.setShelfNo(sourceStock.getContainerNo());
        returnStockBill.setSourceLocation(sourceStock.getStorehouseLocationNo());
        returnStockBill.setDestLocation(destLocation.getNo());
        returnStockBill.setTrayNo(tray.getTrayNo());
        returnStockBill.setOrderStatus(InOrderStatusEnum.NEW.getCode());
        returnStockBillService.insertReturnStockBill(returnStockBill);
        //将回库单的源库位和目标库位锁定
        sourceStock.setStatus(StorehouseStockStatusEnum.LOCK.getCode());
        stockService.updateWmsStorehouseStock(sourceStock);
        WmsStorehouseStock destStock = stockService.selectWmsStorehouseStockByLocationNo(destLocation.getNo());
        if (destStock != null) {
            destStock.setStatus(StorehouseStockStatusEnum.LOCK.getCode());
            stockService.updateWmsStorehouseStock(destStock);
        }
        //创建AGV任务
        agvTaskDispatchService.createTaskDispatch(returnStockBill.getNo(), sourceLocation, destLocation, AgvEnum.TASK_TYPE.BACK,
                sourceStock.getContainerNo(), sourceStock.getTrayNo());
        logger.info("分拣成功创建AGV任务成功：{}", splitBillTrayVo.toString());
        //生成回库单明细
        List<ReturnStockBillDetail> returnBillDetails = new ArrayList<>();
        List<Long> deleteDetails = new ArrayList<>();
        List<TrayDetail> updateDetails = new ArrayList<>();
        for (SplitBillDetail item : splitBillDetails) {
            if (trayDetailMap.containsKey(item.getProductNo() + "_" + item.getBatchNo())) {
                TrayDetail old = trayDetailMap.get(item.getProductNo() + "_" + item.getBatchNo());
                Long qty = old.getQty();
                if (item.getQty() >= qty) {
                    deleteDetails.add(old.getId());
                    trayDetailMap.remove(item.getProductNo());
                    continue;
                }
                ReturnStockBillDetail backBillDetail = generateBackBillDetail(item.getProductNo(), item.getProductName(),
                        item.getProductUnit(), item.getBatchNo(), returnStockBill.getNo(), qty - item.getQty());
                returnBillDetails.add(backBillDetail);
                old.setQty(qty - item.getQty());
                updateDetails.add(old);
                trayDetailMap.remove(item.getProductNo());
            }
        }
        //更新旧托盘明细
        //旧托盘物料被全部取走的明细
        if (deleteDetails.size() != 0) {
            Long[] deleteDetailIds = new Long[deleteDetails.size()];
            deleteDetails.toArray(deleteDetailIds);
            trayDetailService.deleteTrayDetailByIds(deleteDetailIds);
        }
        //旧托盘物料只拿走了部分
        if (updateDetails.size() != 0) {
            trayDetailService.batchUpdateTrayDetail(updateDetails);
        }
        //剩余物料生成了回库明细
        for (String key : trayDetailMap.keySet()) {
            TrayDetail trayDetail = trayDetailMap.get(key);
            ReturnStockBillDetail backBillDetail = generateBackBillDetail(trayDetail.getProductNo(), trayDetail.getProductName(),
                    trayDetail.getProductUnit(), trayDetail.getBatchNo(), returnStockBill.getNo(), trayDetail.getQty());
            returnBillDetails.add(backBillDetail);
        }
        returnStockBillDetailService.batchInsertReturnStockBillDetail(returnBillDetails);
        logger.info("分拣剩余物料：{}", returnBillDetails.toString());
        //生成出库单
        OutStockBill outStockBill = new OutStockBill();
        outStockBill.setNo("OUT" + TableNoUtil.getNo(OutStockBill.class));
        outStockBill.setRelOrderNo(erpOutBillNo);
        outStockBill.setIsSplit("0");
        outStockBill.setShelfNo("");
        outStockBill.setTrayNo(splitBillTrayVo.getNewTrayNo());
        outStockBill.setBillType(splitBillTrayVo.getBillType());
        outStockBill.setAddress(oldOutStockBill.getAddress());
        outStockBill.setOrderStatus(OutOrderStatusEnum.ACCOMPLISH.getCode());
        outStockBill.setRemark(splitBill.getNo());
        outStockBillService.insertOutStockBill(outStockBill);

        List<OutStockBillDetail> outStockBillDetails = new ArrayList<>();
        List<TrayDetail> newTrayDetails = new ArrayList<>();
        for (SplitBillDetail item : splitBillDetails) {
            OutStockBillDetail outStockBillDetail = new OutStockBillDetail();
            outStockBillDetail.setNo(TableNoUtil.getNo(OutStockBillDetail.class));
            outStockBillDetail.setQty(item.getQty());
            outStockBillDetail.setProductNo(item.getProductNo());
            outStockBillDetail.setProductName(item.getProductName());
            outStockBillDetail.setProductUnit(item.getProductUnit());
            outStockBillDetail.setBatchNo(item.getBatchNo());
            outStockBillDetail.setOrderNo(outStockBill.getNo());
            outStockBillDetail.setStatus(OutOrderStatusEnum.NEW.getCode());
            outStockBillDetails.add(outStockBillDetail);

            TrayDetail newTrayDetail = new TrayDetail();
            newTrayDetail.setTrayNo(splitBillTrayVo.getNewTrayNo());
            newTrayDetail.setProductNo(item.getProductNo());
            newTrayDetail.setProductName(item.getProductName());
            newTrayDetail.setBatchNo(item.getBatchNo());
            newTrayDetail.setQty(item.getQty());
            newTrayDetail.setStockStatus(tray.getStockStatus());
            newTrayDetail.setProductUnit(item.getProductUnit());
            newTrayDetails.add(newTrayDetail);
        }
        if (outStockBillDetails.size() != 0) {
            outStockBillDetailService.batchInsertOutStockBillDetail(outStockBillDetails);
            logger.info("分拣后新托盘信息：{}", outStockBillDetails.toString());
        }
        //todo 打印出库单

        //新托盘
//        Tray newTray = trayService.selectTrayByTrayNo(splitBillTrayVo.getNewTrayNo());
        boolean isInsert = false;
        if (newTray == null) {
            newTray = new Tray();
            newTray.setTrayNo(splitBillTrayVo.getNewTrayNo());
            isInsert = true;
        }
        newTray.setOutBillNo(outStockBill.getNo());
        newTray.setErpOutBillNo(erpOutBillNo);
        newTray.setStockStatus(tray.getStockStatus());
        newTray.setBatchNo(tray.getBatchNo());
        if (isInsert) {
            trayService.insertTray(newTray);
        } else {
            trayService.updateTray(newTray);
        }
//        trayDetailService.deleteTrayDetailByTrayNo(splitBillTrayVo.getNewTrayNo());
        if (newTrayDetails.size() != 0) {
            trayDetailService.batchInsertTrayDetail(newTrayDetails);
        }
        logger.info("分拣成功：{}", splitBillTrayVo.toString());
        return toAjax(1);
    }

    /**
     * 生成退库单明细
     *
     * @return
     */
    private ReturnStockBillDetail generateBackBillDetail(String productNo, String productName,
                                                         String productUnit, String batchNo, String billNo, Long qty) {
        ReturnStockBillDetail backBillDetail = new ReturnStockBillDetail();
        backBillDetail.setNo(TableNoUtil.getNo(ReturnStockBillDetail.class));
        backBillDetail.setOrderNo(billNo);
        backBillDetail.setProductName(productName);
        backBillDetail.setProductNo(productNo);
        backBillDetail.setProductUnit(productUnit);
        backBillDetail.setQty(qty);
        backBillDetail.setStatus(InOrderStatusEnum.NEW.getCode());
        backBillDetail.setBatchNo(batchNo);
        return backBillDetail;
    }

    @ApiOperation(value = "执行多单分拣")
    @Log(title = "执行多单分拣", businessType = BusinessType.UPDATE)
    @PostMapping("/newSplit")
    @Transactional
    public AjaxResult newSplit(@RequestBody NewSplitBillTrayVo newSplitBillTrayVo) {
        logger.info("开始多单分拣：{}", newSplitBillTrayVo.toString());
        String key = "IN_LOCK";
        //TODO 临时方案锁，后期处理
        String lock = redisCache.getCacheObject(key);
        if (StringUtils.equals(lock, "true")) {
            throw new RuntimeException("有任务正在进行中，请稍后再试");
        }
        redisCache.setCacheObject(key, "true");
        try {
            return newSplitLock(newSplitBillTrayVo);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
            //TODO 临时方案
            redisCache.setCacheObject(key, "false");
        }
    }

    /**
     * 多单分拣
     *
     * @param newSplitBillTrayVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult newSplitLock(NewSplitBillTrayVo newSplitBillTrayVo) {
        Tray oldTray = trayService.selectTrayByTrayNo(newSplitBillTrayVo.getTrayNo());
        if (oldTray == null) {
            return error("托盘号" + newSplitBillTrayVo.getTrayNo() + "不存在");
        }
        WmsStorehouseStock stock = stockService.selectWmsStorehouseStockByTrayNo(newSplitBillTrayVo.getNewTrayNo());
        if (stock != null) {
            return error("托盘号" + newSplitBillTrayVo.getNewTrayNo() + "在仓库已经存在");
        }
        //ERP单据
        String erpOutBillNo = newSplitBillTrayVo.getBillNo();
        //判断新托盘是否存在数据,如果存在数据,则判断是否是此订单数据
        Tray newTray = trayService.selectTrayByTrayNo(newSplitBillTrayVo.getNewTrayNo());
        if (newTray != null && !StrUtil.equalsIgnoreCase(newTray.getErpOutBillNo(), erpOutBillNo)) {
            return error("托盘号" + newSplitBillTrayVo.getNewTrayNo() + "已经绑定,订单号:" + newTray.getErpOutBillNo() + "的出库单,请更换新托盘");
        }
        String erpAdress = "";
        if (ErpBillTypeEnum.PURCHASE_RETURN.getCode().equals(newSplitBillTrayVo.getBillType())) {
            PurchaseReturnBill purchaseReturnBill = purchaseReturnBillService.selectPurchaseReturnBillByNo(erpOutBillNo);
            if (purchaseReturnBill == null) {
                return error("采购退货单" + erpOutBillNo + "不存在");
            }
            erpAdress = purchaseReturnBill.getCellCode();
        } else if (ErpBillTypeEnum.ADJUST_OUT.getCode().equals(newSplitBillTrayVo.getBillType())) {
            AdjustOutBill adjustOutBill = adjustOutBillService.selectAdjustOutBillByNo(erpOutBillNo);
            if (adjustOutBill == null) {
                return error("调拨出库单" + erpOutBillNo + "不存在");

            }
            erpAdress = adjustOutBill.getAddress();
        } else if (ErpBillTypeEnum.MODULATION_OUT.getCode().equals(newSplitBillTrayVo.getBillType())) {
            ModulationOutBill modulationOutBill = modulationOutBillService.selectModulationOutBillByNo(erpOutBillNo);
            if (modulationOutBill == null) {
                return error("出库调整单" + erpOutBillNo + "不存在");

            }
            erpAdress = modulationOutBill.getCellCode();
        }
        //当前库存和点位
        WmsStorehouseStock sourceStock = stockService.selectWmsStorehouseStockByTrayNo(newSplitBillTrayVo.getTrayNo());
        if (sourceStock == null) {
            return error("托盘号" + newSplitBillTrayVo.getTrayNo() + "库位不存在");
        }
        WmsStorehouseLocation sourceLocation = locationService.selectWmsStorehouseLocationByNo(sourceStock.getStorehouseLocationNo());
        if (sourceLocation == null) {
            return error("托盘号" + newSplitBillTrayVo.getTrayNo() + "库位" + sourceStock.getStorehouseLocationNo() + "不存在");
        }
        if(StringUtils.isEmpty(newSplitBillTrayVo.getOrderNo())){
            return error("分拣单 " + newSplitBillTrayVo.getOrderNo() + "为空");
        }
        //分拣单和分检单明细
        SplitBill splitBill = splitBillService.selectSplitBillByNo(newSplitBillTrayVo.getOrderNo());
        if (splitBill == null) {
            return error("分拣单 " + newSplitBillTrayVo.getOrderNo() + " 未查到");
        }
        if (!StrUtil.equalsIgnoreCase(splitBill.getTrayNo(), oldTray.getTrayNo())) {
            return error("托盘号" + oldTray.getTrayNo() + "与 分拣单 " + splitBill.getNo() + " 无关联");
        }
        SplitBillDetail detailQuery = new SplitBillDetail();
        detailQuery.setOrderNo(newSplitBillTrayVo.getOrderNo());
        detailQuery.setDelFlag("0");
        List<SplitBillDetail> splitBillDetails = splitBillDetailService.selectSplitBillDetailList(detailQuery);
        if (splitBillDetails == null || splitBillDetails.size() == 0) {
            return error("分检单" + newSplitBillTrayVo.getOrderNo() + "不存在分拣单明细数据");
        }
        //分拣单状态更新为已完成
        splitBill.setOrderStatus(OutOrderStatusEnum.ACCOMPLISH.getCode());
        splitBillService.updateSplitBill(splitBill);
        //将旧托盘的出库单据数据清空
        oldTray.setSplitBillNo("");
        oldTray.setErpOutBillNo("");
        oldTray.setOutBillNo("");
        trayService.updateTray(oldTray);
        //回库单物料编号,托盘详情map
        String productNo = "";
        Map<String, TrayDetail> trayDetailMap = new HashMap();
        //旧托盘明细
        TrayDetail query = new TrayDetail();
        query.setTrayNo(newSplitBillTrayVo.getTrayNo());
        query.setDelFlag("0");
        List<TrayDetail> oldTrayDetails = trayDetailService.selectTrayDetailList(query);
        for (TrayDetail item : oldTrayDetails) {
            trayDetailMap.put(item.getProductNo() + "_" + item.getBatchNo(), item);
            productNo = item.getProductNo();
        }
        //需要删除的数据
        List<Long> deleteDetails = new ArrayList<>();
        //需要更新的数据
        List<TrayDetail> updateDetails = new ArrayList<>();
        for (SplitBillDetail item : splitBillDetails) {
            if (trayDetailMap.containsKey(item.getProductNo() + "_" + item.getBatchNo())) {
                TrayDetail old = trayDetailMap.get(item.getProductNo() + "_" + item.getBatchNo());
                Long qty = old.getQty();
                if (item.getQty() >= qty) {
                    deleteDetails.add(old.getId());
                    trayDetailMap.remove(item.getProductNo() + "_" + item.getBatchNo());
                    continue;
                }
                old.setQty(qty - item.getQty());
                updateDetails.add(old);
//                trayDetailMap.put(item.getProductNo() + "_" + item.getBatchNo(),old);
            }
        }
        //旧托盘物料被全部取走的明细
        if (deleteDetails.size() != 0) {
            Long[] deleteDetailIds = new Long[deleteDetails.size()];
            deleteDetails.toArray(deleteDetailIds);
            trayDetailService.deleteTrayDetailByIds(deleteDetailIds);
        }
        //旧托盘物料只拿走了部分
        if (updateDetails.size() != 0) {
            trayDetailService.batchUpdateTrayDetail(updateDetails);
        }
        //查看是否还有剩余的分拣单
        List<TraySplitBillDetailVo> splitBillDetailsByTray = splitBillDetailService.getSplitBillDetailsByTray(oldTray.getTrayNo(), newSplitBillTrayVo.getBillType());
        if (CollectionUtils.isEmpty(splitBillDetailsByTray)) {
            //生成回库单
            ReturnStockBill returnStockBill = new ReturnStockBill();
            returnStockBill.setNo("BACK"+TableNoUtil.getNo(ReturnStockBill.class));
            WmsStorehouseLocation destLocation = emptyStockHelper.analyseInStock(returnStockBill.getNo(), productNo, false, true);
            returnStockBill.setShelfNo(sourceStock.getContainerNo());
            returnStockBill.setSourceLocation(sourceStock.getStorehouseLocationNo());
            returnStockBill.setDestLocation(destLocation.getNo());
            returnStockBill.setTrayNo(oldTray.getTrayNo());
            returnStockBill.setOrderStatus(InOrderStatusEnum.NEW.getCode());
            returnStockBillService.insertReturnStockBill(returnStockBill);
            //将回库单的源库位和目标库位锁定
            sourceStock.setStatus(StorehouseStockStatusEnum.LOCK.getCode());
            stockService.updateWmsStorehouseStock(sourceStock);
            WmsStorehouseStock destStock = stockService.selectWmsStorehouseStockByLocationNo(destLocation.getNo());
            if (destStock != null) {
                destStock.setStatus(StorehouseStockStatusEnum.LOCK.getCode());
                stockService.updateWmsStorehouseStock(destStock);
            }
            //创建AGV任务
            agvTaskDispatchService.createTaskDispatch(returnStockBill.getNo(), sourceLocation, destLocation, AgvEnum.TASK_TYPE.BACK,
                    sourceStock.getContainerNo(), sourceStock.getTrayNo());
            logger.info("分拣成功创建AGV任务成功：{}", newSplitBillTrayVo.toString());
            //剩余物料生成了回库明细
            List<ReturnStockBillDetail> returnBillDetails = new ArrayList<>();
            for (String key : trayDetailMap.keySet()) {
                TrayDetail trayDetail = trayDetailMap.get(key);
                ReturnStockBillDetail backBillDetail = generateBackBillDetail(trayDetail.getProductNo(), trayDetail.getProductName(),
                        trayDetail.getProductUnit(), trayDetail.getBatchNo(), returnStockBill.getNo(), trayDetail.getQty());
                returnBillDetails.add(backBillDetail);
            }
            returnStockBillDetailService.batchInsertReturnStockBillDetail(returnBillDetails);
        }
        //生成出库单
        OutStockBill outStockBill = new OutStockBill();
        outStockBill.setNo("OUT" + TableNoUtil.getNo(OutStockBill.class));
        outStockBill.setRelOrderNo(erpOutBillNo);
        outStockBill.setIsSplit("0");
        outStockBill.setShelfNo("");
        outStockBill.setTrayNo(newSplitBillTrayVo.getNewTrayNo());
        outStockBill.setBillType(newSplitBillTrayVo.getBillType());
        outStockBill.setAddress(erpAdress);
        outStockBill.setOrderStatus(OutOrderStatusEnum.ACCOMPLISH.getCode());
        outStockBill.setRemark(splitBill.getNo());
        outStockBillService.insertOutStockBill(outStockBill);

        List<OutStockBillDetail> outStockBillDetails = new ArrayList<>();
        List<TrayDetail> newTrayDetails = new ArrayList<>();
        for (SplitBillDetail item : splitBillDetails) {
            OutStockBillDetail outStockBillDetail = new OutStockBillDetail();
            outStockBillDetail.setNo(TableNoUtil.getNo(OutStockBillDetail.class));
            outStockBillDetail.setQty(item.getQty());
            outStockBillDetail.setProductNo(item.getProductNo());
            outStockBillDetail.setProductName(item.getProductName());
            outStockBillDetail.setProductUnit(item.getProductUnit());
            outStockBillDetail.setBatchNo(item.getBatchNo());
            outStockBillDetail.setOrderNo(outStockBill.getNo());
            outStockBillDetail.setStatus(OutOrderStatusEnum.NEW.getCode());
            outStockBillDetails.add(outStockBillDetail);

            TrayDetail newTrayDetail = new TrayDetail();
            newTrayDetail.setTrayNo(newSplitBillTrayVo.getNewTrayNo());
            newTrayDetail.setProductNo(item.getProductNo());
            newTrayDetail.setProductName(item.getProductName());
            newTrayDetail.setBatchNo(item.getBatchNo());
            newTrayDetail.setQty(item.getQty());
            newTrayDetail.setStockStatus(oldTray.getStockStatus());
            newTrayDetail.setProductUnit(item.getProductUnit());
            newTrayDetails.add(newTrayDetail);
        }
        if (outStockBillDetails.size() != 0) {
            outStockBillDetailService.batchInsertOutStockBillDetail(outStockBillDetails);
            logger.info("分拣后新托盘信息：{}", outStockBillDetails.toString());
        }

        //新托盘
        boolean isInsert = false;
        if (newTray == null) {
            newTray = new Tray();
            newTray.setTrayNo(newSplitBillTrayVo.getNewTrayNo());
            isInsert = true;
        }
        newTray.setOutBillNo(outStockBill.getNo());
        newTray.setErpOutBillNo(erpOutBillNo);
        newTray.setStockStatus(oldTray.getStockStatus());
        newTray.setBatchNo(oldTray.getBatchNo());
        if (isInsert) {
            trayService.insertTray(newTray);
        } else {
            trayService.updateTray(newTray);
        }
        if (newTrayDetails.size() != 0) {
            trayDetailService.batchInsertTrayDetail(newTrayDetails);
        }
        logger.info("分拣成功：{}", newSplitBillTrayVo.toString());
        return toAjax(1);
    }

    @ApiOperation(value = "整托检")
    @Log(title = "整托检", businessType = BusinessType.UPDATE)
    @PostMapping("/wholePiece")
    @Transactional
    public AjaxResult wholePiece(@RequestBody SplitBillTrayVo splitBillTrayVo) {
        logger.info("开始整托分拣：{}", splitBillTrayVo.toString());
        String key = "IN_LOCK";
        //TODO 临时方案锁，后期处理
        String lock = redisCache.getCacheObject(key);
        if (StringUtils.equals(lock, "true")) {
            throw new RuntimeException("有任务正在进行中，请稍后再试");
        }
        redisCache.setCacheObject(key, "true");
        try {
            return wholePieceLock(splitBillTrayVo);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
            //TODO 临时方案
            redisCache.setCacheObject(key, "false");
        }
    }

    private AjaxResult wholePieceLock(SplitBillTrayVo splitBillTrayVo) {
        Tray tray = trayService.selectTrayByTrayNo(splitBillTrayVo.getTrayNo());
        if (tray == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "不存在");
        }
        WmsStorehouseStock stock = stockService.selectWmsStorehouseStockByTrayNo(splitBillTrayVo.getNewTrayNo());
        if (stock != null) {
            return error("托盘号" + splitBillTrayVo.getNewTrayNo() + "在仓库已经存在");
        }
        //判断新托盘是否存在数据,如果存在数据,则判断是否是此订单数据
        Tray newTray = trayService.selectTrayByTrayNo(splitBillTrayVo.getNewTrayNo());
        if (newTray != null && !StrUtil.equalsIgnoreCase(newTray.getErpOutBillNo(), tray.getErpOutBillNo())) {
            return error("托盘号" + splitBillTrayVo.getNewTrayNo() + "已经绑定,订单号:" + newTray.getErpOutBillNo() + "的出库单,请更换新托盘");
        }
        //判断单据
        String erpOutBillNo = tray.getErpOutBillNo();
        if (ErpBillTypeEnum.PURCHASE_RETURN.getCode().equals(splitBillTrayVo.getBillType())) {
            PurchaseReturnBill purchaseReturnBill = purchaseReturnBillService.selectPurchaseReturnBillByNo(erpOutBillNo);
            if (purchaseReturnBill == null) {
                return error("采购退货单" + erpOutBillNo + "不存在");
            }
        } else if (ErpBillTypeEnum.ADJUST_OUT.getCode().equals(splitBillTrayVo.getBillType())) {
            AdjustOutBill adjustOutBill = adjustOutBillService.selectAdjustOutBillByNo(erpOutBillNo);
            if (adjustOutBill == null) {
                return error("调拨出库单" + erpOutBillNo + "不存在");
            }
        } else if (ErpBillTypeEnum.MODULATION_OUT.getCode().equals(splitBillTrayVo.getBillType())) {
            ModulationOutBill modulationOutBill = modulationOutBillService.selectModulationOutBillByNo(erpOutBillNo);
            if (modulationOutBill == null) {
                return error("出库调整单" + erpOutBillNo + "不存在");
            }
        }
        //判断出库单是否完成
        OutStockBill outStockBillQuery = new OutStockBill();
        outStockBillQuery.setTrayNo(splitBillTrayVo.getTrayNo());
        outStockBillQuery.setDelFlag("0");
        List<OutStockBill> oldStockBills = outStockBillService.selectOutStockBillList(outStockBillQuery);
        if (oldStockBills == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "出库单不存在");
        }
        OutStockBill oldOutStockBill = oldStockBills.get(0);
        if (!OutOrderStatusEnum.ACCOMPLISH.getCode().equals(oldOutStockBill.getOrderStatus())) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "出库单还未完成");
        }
        if (!"0".equals(oldOutStockBill.getIsSplit())) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "不是整托检,请重新扫描托盘号");
        }
        //判断库存和库位
        WmsStorehouseStock sourceStock = stockService.selectWmsStorehouseStockByTrayNo(splitBillTrayVo.getTrayNo());
        if (sourceStock == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "库位不存在");
        }
        WmsStorehouseLocation sourceLocation = locationService.selectWmsStorehouseLocationByNo(sourceStock.getStorehouseLocationNo());
        if (sourceLocation == null) {
            return error("托盘号" + splitBillTrayVo.getTrayNo() + "库位" + sourceStock.getStorehouseLocationNo() + "不存在");
        }
        //旧托盘明细
        TrayDetail query = new TrayDetail();
        query.setTrayNo(splitBillTrayVo.getTrayNo());
        query.setDelFlag("0");
        List<TrayDetail> oldTrayDetails = trayDetailService.selectTrayDetailList(query);
        Map<String, TrayDetail> trayDetailMap = new HashMap();
        for (TrayDetail item : oldTrayDetails) {
            trayDetailMap.put(item.getProductNo() + "_" + item.getBatchNo(), item);
        }
        //将旧托盘的出库单据数据清空
        tray.setSplitBillNo("");
        tray.setErpOutBillNo("");
        tray.setOutBillNo("");
        trayService.updateTray(tray);
        //旧托盘明细删除
        if (oldTrayDetails.size() != 0) {
            Long[] deleteDetailIds = new Long[oldTrayDetails.size()];
            oldTrayDetails.toArray(deleteDetailIds);
            trayDetailService.deleteTrayDetailByIds(deleteDetailIds);
        }
        //新托盘是否存在,不存在则新增
        boolean isInsert = false;
        if (newTray == null) {
            newTray = new Tray();
            newTray.setTrayNo(splitBillTrayVo.getNewTrayNo());
            isInsert = true;
        }
        newTray.setOutBillNo(oldOutStockBill.getNo());
        newTray.setErpOutBillNo(erpOutBillNo);
        newTray.setStockStatus(tray.getStockStatus());
        newTray.setBatchNo(tray.getBatchNo());
        if (isInsert) {
            trayService.insertTray(newTray);
        } else {
            trayService.updateTray(newTray);
        }
        //增加新托盘明细
        List<TrayDetail> newTrayDetails = new ArrayList<>();
        for (TrayDetail item : oldTrayDetails) {
            TrayDetail newTrayDetail = new TrayDetail();
            newTrayDetail.setTrayNo(splitBillTrayVo.getNewTrayNo());
            newTrayDetail.setProductNo(item.getProductNo());
            newTrayDetail.setProductName(item.getProductName());
            newTrayDetail.setBatchNo(item.getBatchNo());
            newTrayDetail.setQty(item.getQty());
            newTrayDetail.setStockStatus(tray.getStockStatus());
            newTrayDetail.setProductUnit(item.getProductUnit());
            newTrayDetails.add(newTrayDetail);
        }
        if (newTrayDetails.size() != 0) {
            trayDetailService.batchInsertTrayDetail(newTrayDetails);
        }
        //删除托盘和料架的绑定关系,让定时器自动生成回库任务
        BindRecord bindRecord = new BindRecord();
        bindRecord.setChildNo(tray.getTrayNo());
        bindRecord.setDelFlag("0");
        List<BindRecord> bindRecords = bindRecordService.selectBindRecordList(bindRecord);
        if (bindRecords.size() != 0) {
            //删除库存表中托盘号
            sourceStock.setTrayNo(null);
            stockService.updateWmsStorehouseStock(sourceStock);
            //删除货架和托盘绑定关系
            List<String> bindRecordNos = bindRecords.stream().map(BindRecord::getParentNo).collect(Collectors.toList());
            bindRecordService.deleteBindRecordByParentNos(bindRecordNos.toArray(new String[bindRecordNos.size()]));
        }
        logger.info("整托分拣成功：{}", splitBillTrayVo.toString());
        return toAjax(1);
    }

}
